﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Urb.Weather.Core.Util;
using Urb.Weather.Core.Provider;
using Urb.Weather.Core.Manager;
using Urb.Weather.Core.MeasureUnit;

namespace Urb.Weather.Manager
{
    internal class WeatherDataHistories : Dictionary<IWeatherDataProviderInstanceRegistrationToken, WeatherDataHistory>
    {
        public WeatherDataHistory GetOrCreate(
            IWeatherDataProviderInstanceRegistrationToken providerInstanceRegistrationToken,
            WeatherDataProviderSourceNames providerSourceNames
            )
        {
            if (!this.ContainsKey(providerInstanceRegistrationToken))
                this[providerInstanceRegistrationToken] = new WeatherDataHistory(providerSourceNames[providerInstanceRegistrationToken]);

            return this[providerInstanceRegistrationToken];
        }


        public AverageTemperatureConditions AverageMostRecentTemperatureConditions
        {
            get
            {
                return CalculateAverageMostRecentConditons<
                    AverageTemperatureConditions,
                    TemperatureConditions,
                    TemperatureUnit>(
                        (wdh) => wdh.Temperature,
                        TemperatureHelper.ConvertTemperature
                );
            }
        }

        public AveragePressureConditions AverageMostRecentPressureConditions
        {
            get
            {
                return CalculateAverageMostRecentConditons<
                    AveragePressureConditions,
                    PressureConditions,
                    PressureUnit>(
                        (wdh) => wdh.Pressure,
                        PressureHelper.ConvertPressure
                );
            }
        }

        private ATC CalculateAverageMostRecentConditons<ATC, Cond, U>(
            Func<WeatherDataHistory, ConditionHistory<U, Cond>> conditionHistorySelector,
            Func<decimal, U, U, decimal> converter)
            where ATC : AverageConditions<Cond, U>, new()
            where Cond : Conditions<U>, new()
        {
            if (!this.Values.Any(wdh => conditionHistorySelector(wdh).MostRecent != null))
                throw new InvalidOperationException(); // can't calculate average!

            var average = new ATC();

            var mostRecentValues = this.Values.Select(wdh => conditionHistorySelector(wdh).MostRecent).Where(mr => mr != null);
            var targetUnit = mostRecentValues.First().Unit;

            average.Unit = targetUnit;
            average.AverageValue =
                mostRecentValues.Average(mr => converter(mr.Value, mr.Unit, targetUnit));
            average.Components.AddRange(mostRecentValues);

            return average;
        }
    }
}
